function extractData(obj){
    return {x:obj.Horsepower, y:obj.Miles_per_Gallon}
}

function removeErrors(obj){
    return obj.x != null && obj.y != null;
}

function tfPlot(values,surface){
    tfvis.render.scatterplot(surface,
        {values:values,series:["Original",'Predicted']},
        {xLabel:'Horsepower',yLabel:"MPG"}
    );
}

async function trainModel(model,inputs,labels,surface){

    const batchSize = 25;
    const epochs = 50;
    const callbacks = tfvis.show.fitCallbacks(surface,['loss'],{callbacks:['onEpochEnd']})

    return await model.fit(inputs,labels,{batchSize,epochs,callbacks});

}

async function runTF(){

    let jsonData = await fetch("./res/carsData.json");
    let values = await jsonData.json();
    values = values.map(extractData).filter(removeErrors);

    // plot the data
    const surface1 = document.getElementById("plot1");
    const surface2 = document.getElementById("plot2");
    const surface3 = document.getElementById("plot3");
    tfPlot(values, surface1);


    // convert input to tensors
    const inputs = values.map(obj => obj.x);
    const labels = values.map(obj => obj.y);
    const inputTensor = tf.tensor2d(inputs,[inputs.length,1]);
    const labelTensor = tf.tensor2d(labels,[labels.length,1]);

    const inputMin = inputTensor.min();
    const inputMax = inputTensor.max();

    const labelMin = labelTensor.min();
    const labelMax = labelTensor.max();

    const nmInputs = inputTensor.sub(inputMin).div(inputMax.sub(inputMin));
    const nmLabels = labelTensor.sub(labelMin).div(labelMax.sub(labelMin));

    const model = tf.sequential();
    model.add(tf.layers.dense({inputShape:[1],units:1,useBias:true}));
    model.add(tf.layers.dense({units:1,useBias:true}));
    model.compile({loss:'meanSquaredError',optimizer:'sgd'});


    await trainModel(model, nmInputs, nmLabels, surface2);


    let unX = tf.linspace(0,1,100);
    let unY = model.predict(unX.reshape([100,1]));

    const unNormunX = unX.mul(inputMax.sub(inputMin)).add(inputMin);
    const unNormunY = unY.mul(labelMax.sub(labelMin)).add(labelMin);

    unX = unNormunX.dataSync();
    unY = unNormunY.dataSync();

    const predicted = Array.from(unX).map((val,i) => {
        return {x:val, y: unY[i]};
    })

    tfPlot([values,predicted], surface3);


}


runTF();